{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Building a Travel Planner with LangGraph: A Tutorial\n",
    "\n",
    "## Overview\n",
    "\n",
    "This tutorial guides you through the process of creating a simple Travel Planner using LangGraph, a library for building stateful, multi-step applications with language models. The Travel Planner demonstrates how to structure a conversational AI application that collects user input and generates personalized travel itineraries.\n",
    "\n",
    "## Motivation\n",
    "\n",
    "In the realm of AI applications, managing state and flow in multi-step processes can be challenging. LangGraph provides a solution by allowing developers to create graph-based workflows that can handle complex interactions while maintaining a clear and modular structure. This Travel Planner serves as a practical example of how to leverage LangGraph's capabilities to build a useful and interactive application.\n",
    "\n",
    "## Key Components\n",
    "\n",
    "1. **StateGraph**: The core of our application, defining the flow of our Travel Planner.\n",
    "2. **PlannerState**: A custom type representing the state of our planning process.\n",
    "3. **Node Functions**: Individual steps in our planning process (input_city, input_interests, create_itinerary).\n",
    "4. **LLM Integration**: Utilizing a language model to generate the final itinerary.\n",
    "\n",
    "## Method Details\n",
    "\n",
    "Our Travel Planner follows a straightforward, three-step process:\n",
    "\n",
    "1. **City Input**: \n",
    "   - The application prompts the user to enter the city they want to visit.\n",
    "   - This information is stored in the state.\n",
    "\n",
    "2. **Interests Input**:\n",
    "   - The user is asked to provide their interests for the trip.\n",
    "   - These interests are stored as a list in the state.\n",
    "\n",
    "3. **Itinerary Creation**:\n",
    "   - Using the collected city and interests, the application leverages a language model to generate a personalized day trip itinerary.\n",
    "   - The generated itinerary is presented to the user.\n",
    "\n",
    "The flow between these steps is managed by LangGraph, which handles the state transitions and ensures that each step is executed in the correct order.\n",
    "\n",
    "## Conclusion\n",
    "\n",
    "This tutorial demonstrates how LangGraph can be used to create a simple yet effective Travel Planner. By structuring our application as a graph of interconnected nodes, we achieve a clear separation of concerns and a easily modifiable workflow. This approach can be extended to more complex applications, showcasing the power and flexibility of graph-based designs in AI-driven conversational interfaces.\n",
    "\n",
    "The Travel Planner serves as a starting point for developers looking to build more sophisticated stateful applications using language models. It illustrates key concepts such as state management, user input handling, and integration with AI models, all within the framework provided by LangGraph."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Setup and Imports\n",
    "\n",
    "First, let's import the necessary modules and set up our environment."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 127,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "from typing import TypedDict, Annotated, List\n",
    "from langgraph.graph import StateGraph, END\n",
    "from langchain_core.messages import HumanMessage, AIMessage\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_openai import ChatOpenAI\n",
    "from langchain_core.runnables.graph import MermaidDrawMethod\n",
    "from IPython.display import display, Image\n",
    "from dotenv import load_dotenv\n",
    "import os\n",
    "load_dotenv()\n",
    "os.environ[\"OPENAI_API_KEY\"] = os.getenv('OPENAI_API_KEY')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Define Agent State\n",
    "\n",
    "We'll define the state that our agent will maintain throughout its operation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "metadata": {},
   "outputs": [],
   "source": [
    "class PlannerState(TypedDict):\n",
    "    messages: Annotated[List[HumanMessage | AIMessage], \"The messages in the conversation\"]\n",
    "    city: str\n",
    "    interests: List[str]\n",
    "    itinerary: str"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Set Up Language Model and Prompts\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 122,
   "metadata": {},
   "outputs": [],
   "source": [
    "llm = ChatOpenAI(model=\"gpt-4o-mini\")\n",
    "\n",
    "\n",
    "itinerary_prompt = ChatPromptTemplate.from_messages([\n",
    "    (\"system\", \"You are a helpful travel assistant. Create a day trip itinerary for {city} based on the user's interests: {interests}. Provide a brief, bulleted itinerary.\"),\n",
    "    (\"human\", \"Create an itinerary for my day trip.\"),\n",
    "])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Define Agent Functions\n",
    "\n",
    "Now we'll define the main functions that our agent will use: get city, get interests, create itinerary"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 123,
   "metadata": {},
   "outputs": [],
   "source": [
    "def input_city(state: PlannerState) -> PlannerState:\n",
    "    print(\"Please enter the city you want to visit for your day trip:\")\n",
    "    user_message = input(\"Your input: \")\n",
    "    return {\n",
    "        **state,\n",
    "        \"city\": user_message,\n",
    "        \"messages\": state['messages'] + [HumanMessage(content=user_message)],\n",
    "    }\n",
    "\n",
    "def input_interests(state: PlannerState) -> PlannerState:\n",
    "    print(f\"Please enter your interests for the trip to {state['city']} (comma-separated):\")\n",
    "    user_message = input(\"Your input: \")\n",
    "    return {\n",
    "        **state,\n",
    "        \"interests\": [interest.strip() for interest in user_message.split(',')],\n",
    "        \"messages\": state['messages'] + [HumanMessage(content=user_message)],\n",
    "    }\n",
    "\n",
    "def create_itinerary(state: PlannerState) -> PlannerState:\n",
    "    print(f\"Creating an itinerary for {state['city']} based on interests: {', '.join(state['interests'])}...\")\n",
    "    response = llm.invoke(itinerary_prompt.format_messages(city=state['city'], interests=\", \".join(state['interests'])))\n",
    "    print(\"\\nFinal Itinerary:\")\n",
    "    print(response.content)\n",
    "    return {\n",
    "        **state,\n",
    "        \"messages\": state['messages'] + [AIMessage(content=response.content)],\n",
    "        \"itinerary\": response.content,\n",
    "    }"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create and Compile the Graph\n",
    "\n",
    "Now we'll create our LangGraph workflow and compile it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 124,
   "metadata": {},
   "outputs": [],
   "source": [
    "workflow = StateGraph(PlannerState)\n",
    "\n",
    "workflow.add_node(\"input_city\", input_city)\n",
    "workflow.add_node(\"input_interests\", input_interests)\n",
    "workflow.add_node(\"create_itinerary\", create_itinerary)\n",
    "\n",
    "workflow.set_entry_point(\"input_city\")\n",
    "\n",
    "workflow.add_edge(\"input_city\", \"input_interests\")\n",
    "workflow.add_edge(\"input_interests\", \"create_itinerary\")\n",
    "workflow.add_edge(\"create_itinerary\", END)\n",
    "\n",
    "app = workflow.compile()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Display the graph structure"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 119,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/jpeg": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(\n",
    "    Image(\n",
    "        app.get_graph().draw_mermaid_png(\n",
    "            draw_method=MermaidDrawMethod.API,\n",
    "        )\n",
    "    )\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Define the function that runs the graph"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 125,
   "metadata": {},
   "outputs": [],
   "source": [
    "def run_travel_planner(user_request: str):\n",
    "    print(f\"Initial Request: {user_request}\\n\")\n",
    "    state = {\n",
    "        \"messages\": [HumanMessage(content=user_request)],\n",
    "        \"city\": \"\",\n",
    "        \"interests\": [],\n",
    "        \"itinerary\": \"\",\n",
    "    }\n",
    "    \n",
    "    for output in app.stream(state):\n",
    "        pass  # The nodes themselves now handle all printing"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Use case example"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 126,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Initial Request: I want to plan a day trip.\n",
      "\n",
      "Please enter the city you want to visit for your day trip:\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Please enter your interests for the trip to paris (comma-separated):\n",
      "Creating an itinerary for paris based on interests: food...\n",
      "\n",
      "Final Itinerary:\n",
      "Here’s a delightful food-focused day trip itinerary for Paris:\n",
      "\n",
      "### Morning\n",
      "- **8:30 AM: Breakfast at Café de Flore**\n",
      "  - Enjoy a classic French breakfast of croissants, café au lait, and fresh orange juice.\n",
      "\n",
      "- **9:30 AM: Visit a Local Bakery (Boulangerie)**\n",
      "  - Stop by **Du Pain et des Idées** for a taste of their famous pain des amis or pistachio croissant.\n",
      "\n",
      "### Late Morning\n",
      "- **10:30 AM: Explore Le Marais District**\n",
      "  - Stroll through the charming streets and pop into specialty food shops, such as **La Maison Plisson** for gourmet snacks.\n",
      "\n",
      "### Lunch\n",
      "- **12:00 PM: Lunch at L'As du Fallafel**\n",
      "  - Savor the best falafel in Paris at this popular spot in Le Marais. Don’t forget to try their famous tahini sauce!\n",
      "\n",
      "### Afternoon\n",
      "- **1:30 PM: Visit the Marché Bastille**\n",
      "  - Explore this vibrant market (open on Sundays) for fresh produce, artisanal cheeses, and local delicacies.\n",
      "\n",
      "- **3:00 PM: Cheese Tasting at Fromagerie Berthaut**\n",
      "  - Sample a variety of French cheeses and learn about the different types from the knowledgeable staff.\n",
      "\n",
      "### Late Afternoon\n",
      "- **4:00 PM: Sweet Treat at Pierre Hermé**\n",
      "  - Indulge in exquisite macarons and pastries from one of Paris's renowned patisseries.\n",
      "\n",
      "- **5:00 PM: Wine Tasting at Ô Chateau**\n",
      "  - Participate in a wine tasting session to learn about and enjoy some of the best French wines.\n",
      "\n",
      "### Evening\n",
      "- **7:00 PM: Dinner at Le Relais de l'Entrecôte**\n",
      "  - Enjoy a classic French steak-frites experience, with their secret sauce and unlimited fries.\n",
      "\n",
      "- **9:00 PM: Dessert at Angelina**\n",
      "  - End your day with their famous hot chocolate and a slice of rich Mont Blanc pastry.\n",
      "\n",
      "### Tips\n",
      "- Consider booking reservations for lunch and dinner to ensure a spot at popular eateries.\n",
      "- Wear comfortable shoes for walking and enjoy the Parisian ambiance between stops!\n",
      "\n",
      "Enjoy your culinary adventure in Paris!\n"
     ]
    }
   ],
   "source": [
    "user_request = \"I want to plan a day trip.\"\n",
    "run_travel_planner(user_request)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Run the Agent\n",
    "\n",
    "Now let's run our agent with a sample request!"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
